#ifndef __COROUTINE_TASK_HPP__
#define __COROUTINE_TASK_HPP__

#include <coroutine>
#include <iostream>
#include <string>

namespace coroutine_task
{
	/**
	 * @brief 协程特定返回类型
	 */
	struct Task
	{
		struct promise_type;
		using promise_handled = std::coroutine_handle<promise_type>;

		//
		struct promise_type
		{
			Task get_return_object()
			{
				std::cout << " 2, get_return_object " << std::endl;

				return Task{ promise_handled::from_promise(*this) };
			}

			std::suspend_never initial_suspend() noexcept
			{
				std::cout << " 4, initial_suspend " << std::endl;

				return {};
			}

			std::suspend_never final_suspend() noexcept
			{
				std::cout << "20, final_suspend " << std::endl;

				return {};
			}

			void unhandled_exception() noexcept
			{
				std::terminate();
			}

			void return_value(int v) noexcept
			{
				std::cout << "19, return_value: " << v << std::endl;
			}

			std::suspend_always yield_value(std::string&& from) noexcept
			{
				std::cout << "14, yield_value: " << from << std::endl;
				m_str = move(from);

				return {};
			}

			std::string m_str;
		};

		//
		Task(promise_handled h)
			: m_handle{ h }
		{
			std::cout << " 3, task construct " << std::endl;
		}

		~Task()
		{
			std::cout << "22, task deconstruct " << std::endl;
			if (m_handle)
			{
				std::cout << "23, task destroy " << std::endl;
				m_handle.destroy();
			}
		}

		std::string value() const
		{
			return m_handle.promise().m_str;
		}

		promise_handled m_handle;
	};

	/**
	 * @brief 协程 co_await关键字需要的awaitable
	 */
	struct awaiter
	{
		awaiter()
		{
			std::cout << " 6, awaiter construct" << std::endl;
		}

		~awaiter()
		{
			std::cout << "11, awaiter deconstruct" << std::endl;
		}

		bool await_ready() noexcept
		{
			std::cout << " 7, await_ready " << std::endl;
			return false;
		}

		void await_resume() noexcept
		{
			std::cout << "10, await_resume " << std::endl;
		}

		void await_suspend(std::coroutine_handle<>) noexcept
		{
			std::cout << " 8, await_suspend " << std::endl;
		}
	};

	/**
	 * @brief 协程函数
	 * @return 协程特定返回类型
	 */
	Task coroutine_task()
	{
		std::cout << " 5, before co_await " << std::endl;
		co_await awaiter{};
		std::cout << "12, after co_await " << std::endl;

		std::cout << "13, before co_yield " << std::endl;
		co_yield "123";
		std::cout << "17, after co_yield " << std::endl;

		std::cout << "18, before co_return " << std::endl;
		co_return 3;
	}

	//
	void test()
	{
		std::cout << " 1, main start" << std::endl;

		auto result = coroutine_task();
		std::cout << " 9, resume co_await-suspend " << std::endl;
		result.m_handle.resume();

		std::cout << "15, get yield_value: " << result.value() << std::endl;

		std::cout << "16, resume co_yield-suspend " << std::endl;
		result.m_handle.resume();

		std::cout << "21, main end" << std::endl;
	}
}

#endif